﻿#r "FSharp.PowerPack.Parallel.Seq"
#r "Microsoft.VisualStudio.QualityTools.UnitTestFramework"
#load "Viterbi.fs"
open FSViterbi
open System
open Microsoft.VisualStudio.TestTools.UnitTesting

//create observable from sequence
//http://stackoverflow.com/questions/3845110/observable-from-sequence-in-f
module Observable =  
    let guard f (e:IObservable<'Args>) =   
      { new IObservable<'Args> with   
          member x.Subscribe(observer) =   
            let rm = e.Subscribe(observer) in f(); rm }  
    let ofSeq s =  
        let evt = new Event<_>() 
        evt.Publish |> guard (fun o -> 
            for n in s do evt.Trigger(n)) 
 

type States = Rainy | Sunny
type Observations = Walk | Shop | Clean

let rainy, rainyToRainy, rainyWalk, rainyShop = 0.6, 0.7, 0.1, 0.4
let rainyClean = 1.0 - rainyWalk - rainyShop
let rainyToSunny = 1.0 - rainyToRainy

let sunny, sunnyToRainy, sunnyWalk, sunnyShop = 1.0 - rainy, 0.4, 0.6, 0.3
let sunnyClean = 1.0 - sunnyWalk - sunnyShop
let sunnyToSunny = 1.0 - sunnyToRainy

let startingProbs = [Rainy, rainy; Sunny, sunny] |> Map.ofList

let transitionProbs = 
    [(Rainy,Rainy), rainyToRainy
     (Rainy,Sunny), rainyToSunny
     (Sunny,Rainy), sunnyToRainy
     (Sunny,Sunny), sunnyToSunny]
    |> Map.ofSeq

let emissionProbs =
    [(Rainy, Walk), rainyWalk
     (Rainy, Shop), rainyShop
     (Rainy, Clean), rainyClean
     (Sunny, Walk), sunnyWalk
     (Sunny, Shop), sunnyShop
     (Sunny, Clean), sunnyClean]
    |> Map.ofSeq

let fStartProbs s = startingProbs.[s]
let fTransitionProbs s_pair = transitionProbs.[s_pair]
let fEmissionProbs s_o_pair = emissionProbs.[s_o_pair]

let fbv = Viterbi(fStartProbs, fTransitionProbs, fEmissionProbs) 

let obs = [Walk; Shop; Clean] //sequence of observations
let allStates = seq [Rainy; Sunny]
let obsStates = seq {for o in obs -> (o, allStates)}

let obervable = obsStates |> Observable.ofSeq

obervable.Subscribe(fbv :> IObserver<Observations*States seq>)

let finalProbs = fbv.V.Head

Assert.AreEqual(Math.Round(finalProbs.[Rainy], 6), 0.013440);
Assert.AreEqual(Math.Round(finalProbs.[Sunny], 6), 0.002592);

let mostProbableState = finalProbs |> Seq.maxBy (fun kv -> kv.Value)

Assert.AreEqual(mostProbableState.Key, Rainy)

